﻿#include "package.h"
#include "dhlz_type.h"
#include "mycrc.h"
#include <QApplication>
#include <QWidget>
#include <QDir>
#include <QFile>
#include <QTextStream>
#include <QDebug>
#include <QDateTime>

package::package(void)
{
    p_file_ram = NULL;
    file_ram_size = 0;
    file_count = 0;
    add_offset = 0;
    file_ram_len = 0;
    out_file.clear();
}

package::~package()
{
    if (p_file_ram != NULL)
    {
        delete [] p_file_ram;
    }

    p_file_ram = NULL;
}

void package::reset(QString pathfile)
{
    out_file = pathfile;
    file_ram_size = 10 * 1024 * 1024;
    file_count = 0;
    add_offset = 0;
    file_ram_len = 0;
    p_file_ram = new char[file_ram_size];
    head_size = ((sizeof(file_head_t) + 0x0F) & 0x7F0);
    file_info_size = ((sizeof(file_info_t) + 0x07) & 0x3F8);
}

void package::set_offset(unsigned short offset)
{
    qDebug() << "set_offset" << offset;
    add_offset = offset;
}

void package::set_count(unsigned short count)
{
    qDebug() << "set_count" << count;
    file_count = count;
}

void package::push(int index, const file_dir_info *p_info)
{
    if ((index < file_count) && (p_file_ram != NULL))
    {
        qDebug() << "package::push" << index << "path:" << p_info->path << "name:" << p_info->name;
        struct file_info_t *info = (struct file_info_t *)(p_file_ram + head_size + file_info_size * index);
        memset(info, 0, file_info_size);
        int name_len = strlen(p_info->name) > (sizeof(info->name) - 1) ? (sizeof(info->name) - 1) : strlen(p_info->name);
        memcpy(info->name, p_info->name, name_len);
        hex32_to_str(p_info->adder + add_offset, (char *)&info->adder_offset);
        hex32_to_str(p_info->size, (char *)&info->size);
        hex32_to_str(p_info->crc, (char *)&info->crc32);
        hex16_to_str(p_info->w, (char *)&info->picture_w);
        hex16_to_str(p_info->h, (char *)&info->picture_h);

        if ((p_info->adder + add_offset + p_info->size) > file_ram_len)
        {
            file_ram_len = p_info->adder + add_offset + p_info->size;
        }

        file_cpy(info, p_info);
    }
    else
    {
        qDebug() << "package::push fail" << index << file_count;
    }
}

bool package::check(QString qstr_ver)
{
    file_head_t head;
    memset(&head, 0, sizeof(file_head_t));
    // 获取当前日期和时间
    QDateTime currentDateTime = QDateTime::currentDateTime();
    // 格式化当前日期和时间为字符串
    QString curtime = currentDateTime.toString("yyyy-MM-dd hh:mm:ss");
    head.head_size = head_size;
    memcpy(head.tag, "DHLZ", 4);
    QByteArray byteArray = curtime.toUtf8();
    const char *ctime = byteArray.data();
    memcpy(head.time, ctime, strlen(ctime));

    if (qstr_ver.length() > 3)
    {
        QByteArray byteArray = qstr_ver.toUtf8();
        const char *cver = byteArray.data();
        int ver_len = strlen(cver) > (sizeof(head.ver) - 2) ? (sizeof(head.ver) - 2) : strlen(cver);
        memcpy(head.ver, cver, ver_len);
    }

    hex16_to_str(file_count, (char *)&head.file_number); /* 文件数 */
    hex16_to_str(file_info_size, (char *)&head.file_info_size); /* 文件信息大小 */
    mycrc crc32;
    crc32.reset();
    crc32.push(p_file_ram + head_size, file_info_size * file_count);
    hex32_to_str(crc32.value32(), (char *)&head.file_info_crc32); /* 文件信息校验 */
    file_ram_len = ((file_ram_len + 0xFFF) & 0xFFFF000);    /* 4Kbyte对齐 */
    hex32_to_str(add_offset, (char *)&head.file_data_offset); /* 文件内容起始偏移 */
    hex32_to_str(file_ram_len - add_offset, (char *)&head.file_data_size); /* 文件内容大小 */
    crc32.reset();
    crc32.push(p_file_ram + add_offset, file_ram_len - add_offset);
    hex32_to_str(crc32.value32(), (char *)&head.file_data_crc32); /* 文件内容校验 */
    crc32.reset();
    crc32.push(&head, sizeof(file_head_t) - sizeof(head.head_crc32));
    hex32_to_str(crc32.value32(), (char *)&head.head_crc32); /* 头信息校验 */
    memcpy(p_file_ram, &head, sizeof(file_head_t));

    if (check_ram_info() && check_ram_file())
    {
        QFile file(out_file);// 创建文件对象

        // 打开文件，以写入模式和二进制模式打开
        if (file.open(QIODevice::WriteOnly))
        {
            QDataStream out(&file);// 创建数据流
            out.writeRawData(p_file_ram, file_ram_len);// 写入数据
            file.close();// 关闭文件
            return true;
        }
    }

    return true;
}

bool package::check_ram_info(void)  /* 校验头信息、目录 */
{
    if (p_file_ram == NULL)
    {
        return false;
    }

    file_head_t head;
    memcpy(&head, p_file_ram, sizeof(file_head_t));
    unsigned short head_size = str_to_hex16((char *)&head.head_size);
    unsigned short file_number = str_to_hex16((char *)&head.file_number);
    unsigned short file_info_size = str_to_hex16((char *)&head.file_info_size);
    unsigned long file_data_offset = str_to_hex32((char *)&head.file_data_offset);
    unsigned long file_data_size = str_to_hex32((char *)&head.file_data_size);

    if ((head.tag[0] != 'D')
        || (head.tag[1] != 'H')
        || (head.tag[2] != 'L')
        || (head.tag[3] != 'Z')
        || (head.tag[4] != 0))
    {
        qDebug() << "package::check_ram tag fail" << head.tag;
        return false;
    }

    qDebug() << "package::check_ram ok 1";
    mycrc crc32;
    crc32.reset();
    crc32.push(p_file_ram + head_size, file_info_size * file_number);

    if (crc32.value32() != str_to_hex32((char *)&head.file_info_crc32))
    {
        qDebug() << "package::check_ram file_info_crc32 fail" << crc32.value32() << str_to_hex32((char *)&head.file_info_crc32);
        return false;
    }

    qDebug() << "package::check_ram ok 2" << file_data_offset << file_data_size;
    crc32.reset();
    crc32.push(p_file_ram + file_data_offset, file_data_size);

    if (crc32.value32() != str_to_hex32((char *)&head.file_data_crc32))
    {
        qDebug() << "package::check_ram file_data_crc32 fail" << crc32.value32() << str_to_hex32((char *)&head.file_data_crc32);
        return false;
    }

    qDebug() << "package::check_ram ok 3";
    crc32.reset();
    crc32.push(p_file_ram, sizeof(file_head_t) - sizeof(head.head_crc32));

    if (crc32.value32() != str_to_hex32((char *)&head.head_crc32))
    {
        qDebug() << "package::check_ram head_crc32 fail" << crc32.value32() << str_to_hex32((char *)&head.head_crc32);
        return false;
    }

    return true;
}

bool package::check_ram_file(void)  /* 校验文件内容 */
{
    bool ret = true;
    file_head_t head;
    memcpy(&head, p_file_ram, sizeof(file_head_t));
    unsigned short head_size = str_to_hex16((char *)&head.head_size);/* 头信息大小(byte)n*16 */
    unsigned short file_number = str_to_hex16((char *)&head.file_number);/* 文件数 */
    unsigned short file_info_size = str_to_hex16((char *)&head.file_info_size);/* 文件信息大小 */
    unsigned long file_data_offset = str_to_hex32((char *)&head.file_data_offset);/* 文件内容起始偏移 */
    unsigned long file_data_size = str_to_hex32((char *)&head.file_data_size);/* 文件内容大小 */
    qDebug() << "check_ram_file" << head_size << file_number << file_info_size << file_data_offset << file_data_size;

    for (int i = 0; i < file_number; i++)
    {
        file_info_t info;
        memcpy(&info, p_file_ram + head_size + i * sizeof(file_info_t), sizeof(file_info_t));
        unsigned long  info_offset = str_to_hex32((char *)&info.adder_offset);/* 文件偏移地址 */
        unsigned long  info_size = str_to_hex32((char *)&info.size);
        unsigned long info_crc32 = str_to_hex32((char *)&info.crc32);        /* 文件内容校验 */

        if ((info_offset + info_size - file_data_offset) <= file_data_size)
        {
            mycrc crc32;
            crc32.reset();
            crc32.push(p_file_ram + info_offset, info_size);

            if (crc32.value32() != info_crc32)
            {
                qDebug() << "check_ram_file crc fail " << i << crc32.value32() << info_crc32 << info.name;
                ret = false;
                break;
            }
        }
        else
        {
            qDebug() << "check_ram_file offset fail " << info_offset << info_size << file_data_offset << file_data_size;
            ret = false;
            break;
        }
    }

    return ret;
}

bool package::file_cpy(const file_info_t *pfile, const file_dir_info *p_dir)
{
    unsigned long adder_offset = str_to_hex32((char *)&pfile->adder_offset);
    unsigned long file_size = str_to_hex32((char *)&pfile->size);

    if ((adder_offset + file_size) > file_ram_size) /* 判断当前缓存是否放的下 */
    {
        qDebug() << "package::file_cpy add_size";
        int new_size = file_ram_size + 5 * 1024 * 1024;
        char *new_ram = new char[new_size];
        memset(new_ram, 0, new_size);
        memcpy(new_ram, p_file_ram, file_ram_size);
        delete [] p_file_ram;
        p_file_ram = new_ram;
        file_ram_size = new_size;
    }

    QDir dir(QString::fromUtf8(p_dir->path));
    QString fullPath = dir.filePath(QString::fromUtf8(p_dir->name));
    // 创建 QFile 对象并打开二进制文件
    QFile file(fullPath);

    if (!file.open(QIODevice::ReadOnly))
    {
        qDebug() << "package::file_cpy open fail" << fullPath << "path" << p_dir->path << "name" << p_dir->name;
        return false;
    }

    QByteArray data = file.readAll();// 读取文件内容
    unsigned long size = file.size();
    file.close();// 关闭文件
    memcpy(p_file_ram + adder_offset, data.constData(), size);
    return true;
}

unsigned short package::str_to_hex16(const char *str)
{
    return ((str[0] & 0x0FF) | ((str[1] << 8) & 0x0FF00));
}

unsigned long package::str_to_hex32(const char *str)
{
    return ((str[0] & 0x0FF) | ((str[1] << 8) & 0x0FF00) | ((str[2] << 16) & 0x0FF0000) | ((str[3] << 24) & 0xFF000000));
}

void package::hex16_to_str(unsigned short value, char *str)
{
    str[0] = value & 0x0FF;
    str[1] = (value >> 8) & 0x0FF;
}

void package::hex32_to_str(unsigned long value, char *str)
{
    str[0] = value & 0x0FF;
    str[1] = (value >> 8) & 0x0FF;
    str[2] = (value >> 16) & 0x0FF;
    str[3] = (value >> 24) & 0x0FF;
}
